/*
    socklibpp library
    Copyright (C) 2005  Daniel K. O. <danielosmari at users.sf.net>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifndef SELECT_TYPE_ARG234
#define SELECT_TYPE_ARG234 (fd_set*)
#endif

#include <socklibpp/sockbase.h>

namespace socklib {

/**     \file sockbase.cpp
        \brief base implementation.
*/


/**
        \class base
        This is a portability low-level wrapper class; it only stores the
        socket descriptor and the error code of the last call, to maintain
        a small footprint.

        No exceptions are thrown. Most of the methods return "true" if the
        operation succeeded. All methodds set the base::error member
        to indicate the error code of the last call; if it's zero, the operation
        succeeded.

        Its instance doesn't own the socket descriptor (i.e. the
        destructor doesn't close the socket, and the default constructor
        doesn't create a socket). So you can copy base objects how many
        times you want to. Remember to actualy create the socket when needed,
        and to close it before destoying it.
        
*/

/**
        \param fmt      an address format
        \param type     socket type
        \param proto    protocol for this socket
        \sa format, type, protocol
*/
base::base(     base::format fmt,
                base::type type,
                base::protocol proto)
throw() :
        error(0)
{
        create(fmt, type, proto);
}


/**
        Before calling this method the socket should be in listening mode, by
        calling listen().

        \return A new base object representing the new connection; if an
                error occurs, the fd member has the value invalid_socket.

        \note   You should always check the error member to see if the operation
                succeeded. In case of errors, the base returned is invalid,
                and should be ignored.
                
        \sa listen()
*/
base
base::accept()
throw ()
{
        socket_type s = ::accept(fd, 0, 0);
        
        error = s==invalid_socket ? __GET_LAST_ERROR : 0;

        return base(s);
}


/**
        Before calling this method the socket should be in listening mode, by
        calling listen().

        \param sa       a sock_addr object to store the address of the accepted
                        connection. For internet sockets this should be a
                        sock_addr_in object.
        \return A new base object representing the new connection; if an
                error occurs, the fd member has the value invalid_socket.

        \note   You should always check the error member to see if the operation
                succeeded. In case of errors, the base returned is invalid,
                and should be ignored.
        \sa listen()
*/
base
base::accept(addr& sa)
throw ()
{
        sa.reset();
        socklen_t sz = sa.size();
        socket_type s = ::accept(fd, sa.data(), &sz);

        error = s==invalid_socket ? __GET_LAST_ERROR : 0;

        if (!error)
                sa.size(sz);

        return base(s);
}


/**     \param sa       a sock_addr object with the address to bind the socket.
                        For internet sockets this should be a sock_addr_in
                        object.
        \return true if no error occurred
*/
bool
base::bind(const addr& sa)
throw()
{
        int r = ::bind(fd, sa.data(), sa.size());
        
        error = r ? __GET_LAST_ERROR : 0;

        return !error;
}


/* TODO (DanielKO#2#): Add more documentation when implementing 
                       SO_LINGER */
/**     \return true if no error occurred
*/
bool
base::close()
throw()
{
        int r = ::closesocket(fd);

        error = r ? __GET_LAST_ERROR : 0;
        
        return !error;
}


/**     \param sa       a valid sock_addr to make a connection to. For
                        internet sockets it should be a sock_addr_in object.
        \return true if no error occurred
*/
bool
base::connect(const addr& sa)
throw()
{
        int r = ::connect(fd, sa.data(), sa.size());
        
        error = r ? __GET_LAST_ERROR : 0;
        
        return !error;
}


/**
        It is used on a connected datagram or stream socket.
        
        \param sa       a sock_addr object to where store the address. For
                        internet sockets it should be a sock_addr_in object.
        \return true if no error occurred
*/
bool
base::peername(addr& sa) const
throw()
{
        sa.reset();
        socklen_t sz = sa.size();
        int r = ::getpeername(fd, sa.data(), &sz);
        
        error = r ? __GET_LAST_ERROR : 0;

        if (!r)
                sa.size(sz);
        
        return !error;
}


/**
        This call is especially useful when a listen() call has been made
        without doing a bind() first.
        
        \param sa       a sock_addr object to where store the address. For
                        internet sockets it should be a sock_addr_in object.
        \return true if no error ocurred

        \note   If the socket was bound to addr_any, indicating that any of
                the host's IP addresses should be used for the socket,
                sockname() will not necessarily return information about the
                host IP address, unless the socket has been connected with
                connect() or accept().
*/
bool
base::sockname(addr& sa)
throw()
{
        sa.reset();

        socklen_t sz = sa.size();
        int r = ::getsockname(fd, sa.data(), &sz);

        error = r ? __GET_LAST_ERROR : 0;

        if (!r)
                sa.size(sz);

        return !error;
}


/**
        Use this for options so_error, so_rcvbuf, so_sndbuf and so_type.

        \param level    the optlevel at which the option is defined. Must be
                        sol_socket or ipproto_tcp.
        \param name     an optname option
        \param val      int where to store the value for the specified option
        \return true if no error ocurred

        \sa optlevel, optname, setsockopt
*/
bool
base::getsockopt(base::optlevel level, base::optname name, int& val) const
throw()
{
        socklen_t sz = sizeof val;
        int r = ::getsockopt(fd, level, name, reinterpret_cast<char*>(&val), &sz);

        error = r ? __GET_LAST_ERROR : 0;

        return !error;
}


/**
        Use this for so_acceptconn, so_broadcast, so_debug, so_dontlinger,
        so_dontroute, so_keepalive, so_oobinline, so_reuseaddr and tcp_nodelay.

        \param level    the optlevel at which the option is defined. Must be
                        sol_socket or ipproto_tcp.
        \param name     an optname option
        \param val      bool where to store the value for the specified option
        \return true if no error ocurred

        \sa optlevel, optname, setsockopt
*/
bool
base::getsockopt(base::optlevel level, base::optname name, bool& val) const
throw()
{
        int _val;
        socklen_t sz = sizeof _val;
        int r = ::getsockopt(fd, level, name, reinterpret_cast<char*>(&_val), &sz);
        
        error = r ? __GET_LAST_ERROR : 0;
        
        if (!error)
                val = _val;
        
        return !error;
}


/**     \param cmd      fionbio, fionread or siocatmark
        \param val      where to obtain or place the parameter
        \return true if no error occurred

        It may be used on any socket in any state. It is used to retrieve
        parameters independent of the protocol and communication subsystem.
        \sa getsockopt(), setsockopt()
*/
bool
base::ioctl(base::ioctl_cmd cmd, unsigned long& val)
throw()
{
        int res = ::ioctlsocket(fd, cmd, &val);
        
        error = res ? __GET_LAST_ERROR : 0;
        
        return !error;
}


/**     \param backlog  The maximum length to which the queue of pending
                        connections may grow. On some systems it may be
                        limited to small values (like 5). Illegal values are
                        replaced by the nearest legal value.
        \return true if no error occurred
        
        To accept connections the socket must first be created with create(),
        a backlog is specified with listen(), and the connections are accepted
        with accept(). listen() only applies to sockets that support connections,
        i.e. sock_stream.

        This is typically used by servers that could have more than one
        connection request at a time: if a connection request arrives with the
        queue full, the client will receive an error with an indication of
        econnrefused.

        \sa accept(), bind(), connect()
*/
bool
base::listen(int backlog)
throw()
{
        int r = ::listen(fd, backlog);
        
        error = r ? __GET_LAST_ERROR : 0;
        
        return !error;
}


/**     This is used on connected datagram or sockstream sockets.

        For sockets of type sock_stream, as much information as is currently
        available up to the size of the buffer supplied is returned. If the
        socket has been configured for in-line reception of out-of-band data
        (socket option so_oobinline) and out-of-band data is unread, only
        out-of-band data will be returned. The application may use the
        ioctl() siocatmark to determine whether any more out-of-band data
        remains to be read.

        For datagram sockets (sock_dgram), data is extracted from the first
        enqueued datagram, up to the size of the size of the buffer supplied.
        If the datagram is larger than the buffer supplied, the excess data is
        lost, and the error member becomes emsgsize.

        If no incoming data is available at the socket, the recv() call waits
        for data to arrive unless the socket is non-blocking.  In this case a
        value of socket_error is returned with the error member set to
        ewouldblock. The can_read() method may be used to determine when more
        data arrives.

        If the socket is of type sock_stream and the remote side has shut down
        the connection gracefully, a recv() will complete immediately with 0
        bytes received. If the connection has been abortively disconnected, a
        recv() will fail with the error member set to econnreset.

        \param buf      A buffer for the incoming data.
        \param bs       The buffer size. The buffer may be first tested for
                        writing permission, so never pass a size bigger than
                        the real buffer's size.
        \param flags    Specifies the way in which the call is made. Can be any
                        or-combination of msg_none, msg_peek or msg_oob.

        \retval number of bytes received, if no error occurs
        \retval 0 if connection was closed
        \retval socket_error if an error ocurred
        
        \sa recvfrom(), recvall(), can_read(), create(), block()
*/
int
base::recv(char* buf, int bs, base::msgflag flags)
throw()
{
        int r = ::recv(fd, buf, bs, flags);
        
        error = /*r==socket_error ?*/ __GET_LAST_ERROR /*: 0*/;
        
        return r;
}


/**
        This function is used to read incoming data on a (possibly connected)
        socket and capture the address from which the data was sent.

        For sockets of type sock_stream, as much information as is currently
        available up to the size of the buffer supplied is returned. If the
        socket has been configured for in-line reception of out-of-band data
        (socket option so-oobinline) and out-of-band data is unread, only
        out-of-band data will be returned. The application may use the
        ioctl() siocatmark to determine whether any more out-of-band data
        remains to be read. The from parameter is ignored for sock_stream
        sockets.

        For datagram sockets, data is extracted from the first enqueued
        datagram, up to the size of the size of the buffer supplied. If the
        datagram is larger than the buffer supplied, the buffer is filled with
        the first part of the message, the excess data is lost, and recvfrom()
        sets the error member to emsgsize.

        The network address of the peer which sent the data is copied to the
        corresponding sock_addr object.

        If no incoming data is available at the socket, the recvfrom() call
        waits for data to arrive unless the socket is non-blocking. In this case
        a value of socket_error is returned with the error member set to
        ewouldblock. The can_read() method may be used to determine when more
        data arrives.

        If the socket is of type sock_stream and the remote side has shut down
        the connection gracefully, a recvfrom() will complete immediately with
        0 bytes received. If the connection has been abortively disconnected,
        a recvfrom() will fail with the error member set to econnreset.

        Flags may be used to influence the behavior of the function invocation
        beyond the options specified for the associated socket. That is, the
        semantics of this function are determined by the socket options and the
        flags parameter.

        \param from     A sock_addr object. For internet sockets it should be
                        a sock_addr_in object.
        \param buf      A buffer for the incoming data.
        \param bs       The buffer size. The buffer may be first tested for
                        writing permission, so never pass a size bigger than
                        the real buffer's size.
        \param flags    Specifies the way in which the call is made. Can be any
                        or-combination of msg_none, msg_peek or msg_oob.

        \retval number of bytes received, if no error occurs
        \retval 0 if connection was closed
        \retval socket_error if an error ocurred

        \sa recv(), recvall(), can_read(), create(), block()
*/
int
base::recvfrom(addr& from, char* buf, int bs, base::msgflag flags)
throw()
{
        from.reset();
        socklen_t sz = from.size();

        int r = ::recvfrom(fd, buf, bs, flags, from.data(), &sz);

        error = r==socket_error ? __GET_LAST_ERROR : 0;
        
        if (!error)
                from.size(sz);
        
        return r;
}


/**
        \param usec     microseconds to wait until the socket can be read.
        \param sec      seconds to wait until the socket can be read. Set it to
                        -1 if you want it to block.

        \retval true For listening sockets, returns true if an incomming request has
                been received, so accept is guaranteed to complete without
                blocking.
                For other sockets, returns true if there's queued data available
                for reading (recv() or recvfrom())
        \retval false if the timeout given by sec and usec already passed out and
                no data was available, or an error ocurred. Always check the error
                member.
                
        \note   For sock_stream sockets, can_read() will also return true if the
                connection has been closed, so the next recv or recvfrom will
                complete without blocking. If the connection was closed
                gracefully, then a recv() will return with 0 bytes read; if the
                connectino was closed abortively, recv() will return socket_error,
                and the error member will be set to econnreset
                The presence of out-of-band data will be checked if the socket
                option SO_OOBINLINE has been enabled.


        \sa ioctl(), setsockopt(), accept(), recv()
*/
bool
base::can_read(int usec, int sec)
throw()
{
        fd_set set;

        FD_ZERO(&set);
        FD_SET(fd, &set);
        
        struct timeval tv = { sec, usec };
        
        int r = ::select(FD_SETSIZE, SELECT_TYPE_ARG234 &set, 0, 0, &tv);
        
        error = r==socket_error ? __GET_LAST_ERROR : 0;
        
        if (error || !FD_ISSET(fd, &set))
                return false;
                
        return true;
}

/**
        \param usec     microseconds to wait until the socket can be written.
        \param sec      seconds to wait until the socket can be written. Set it to
                        -1 if you want it to block.

        \retval true    if a (non-blocking) socket is connecting the connection
                        establishment is complete.
        \retval true    for other sockets a send() or sendto() will complete
                        without blocking. (It is not specified how long this
                        guarantee can be assumed to be valid, particularly in a
                        multithreaded environment.)
        \retval false   if the timeout given by usec and sec already passed out
                        and no data was available, or an error ocurred. Always
                        check the error data member.

        \sa ioctl(), setsockopt(), connect(), send()
*/
bool
base::can_write(int usec, int sec)
throw()
{
        fd_set set;

        FD_ZERO(&set);
        FD_SET(fd, &set);

        struct timeval tv = { sec, usec };

        int r = ::select(FD_SETSIZE, 0, SELECT_TYPE_ARG234 &set, 0, &tv);

        error = r==socket_error ? __GET_LAST_ERROR : 0;

        if (error || !FD_ISSET(fd, &set))
                return false;

        return true;
}


/**
        \param usec     microseconds to wait until the socket receives an
                        exceptional event
        \param sec      seconds to wait until the socket receives an exceptional
                        event. Set it to -1 if you want it to block.

        \retval true    There's out-of-band data (see below) to be read
        \retval true    The connection of a sock_stream was broken or a
                        KEEPALIVE failure.
        \retval true    A connect() attempt on a (non-blocking) socket has
                        failed.
        \retval false   No exceptional condition found, or an error ocurred.
                        Always check the error member.

        \note   Out-of-band data will only be reported in this way if the option
                so_oobinline is false.

        \sa ioctl(), setsockopt(), connect()
*/
bool
base::can_except(int usec, int sec)
throw()
{
        fd_set set;

        FD_ZERO(&set);
        FD_SET(fd, &set);

        struct timeval tv = { sec, usec };

        int r = ::select(FD_SETSIZE, 0, 0, SELECT_TYPE_ARG234 &set, &tv);

        error = r==socket_error ? __GET_LAST_ERROR : 0;

        if (error || !FD_ISSET(fd, &set))
                return false;

        return true;
}


/**
        Is used on connected datagram or stream sockets and is used to
        write outgoing data on a socket.

        If no buffer space is available within the transport system to hold the
        data to be transmitted, send() will block unless the socket has been
        placed in a non-blocking I/O mode.

        The can_write() method may be used to determine when it is possible to
        send more data.
        
        \param buf      A buffer containing the data to be transmited.
        \param len      The length of the data in buf.
        \param flags    Flags to be used to influence the behavior of the
                        send() method. Can be any or-combination of msg_none,
                        msg_dontroute and msg_oob (sock_stream only).

        \retval [1,len] For sock_stream sockets, indicating how much data was
                        sent.
        \retval [0,len] For other sockets, indicating how much data was sent.
        \retval socket_error    An error ocurred. Check the error code in the
                                error data member.

        \note   For datagram sockets, care must be taken not to exceed the
                maximum IP packet size of the underlying subnets If the data
                is too long to pass atomically through the underlying protocol
                the error emsgsize is set on the error member, and no data is
                transmitted.
        \note   The successful completion of a send() does not indicate that
                the data was successfully delivered.

        \sa sendto(), can_write(), ioctl(), setsockopt()
*/
int
base::send(const char *buf, int len, base::msgflag flags)
throw()
{
        int r = ::send(fd, buf, len, flags);
        
        error = r==socket_error ? __GET_LAST_ERROR : 0;

        return r;
}


/**
        Is used on datagram or stream sockets and is used to write outgoing data
        on a socket.

        This is normally used on a sock_dgram socket to send a datagram to a
        specific peer socket identified by the to parameter. On a sock_stream
        socket, the to parameter is ignored; in this case the sendto() is
        equivalent to send().

        If no buffer space is available within the transport system to hold the
        data to be transmitted, sendto() will block unless the socket has been
        placed in a non-blocking I/O mode.

        The can_write() method may be used to determine when it is possible to
        send more data.
        
        \param to       Address of the target socket. For internet sockets this
                        should be a sock_addr_in object
        \param buf      A buffer containing the data to be transmited
        \param len      Length of the data in buf
        \param flags    Flags to be used to influence the behavior of the sendto()
                        method. Can be any or-combination of msg_none,
                        msg_dontroute and msg_oob (sock_stream only).
        
        \retval [1,len] For non-blocking sock_stream sockets, indicating how
                        much data was sent.
        \retval [0,len] For other sockets, indicating how much data was sent.
        \retval socket_error    An error ocurred. Check the error code in the
                                error data member.

        \note   For datagram sockets, care must be taken not to exceed the
                maximum IP packet size of the underlying subnets If the data
                is too long to pass atomically through the underlying protocol
                the error emsgsize is set on the error member, and no data is
                transmitted.
        \note   To send a broadcast (on a sock_dgram only), the address in the to
                parameter should be constructed using the special IP address
                addr_broadcast (defined in sockaddr.h) together with the intended port
                number. It is generally inadvisable for a broadcast datagram to exceed
                the size at which fragmentation may occur, which implies that the data
                portion of the datagram (excluding headers) should not exceed 512 bytes.
        \note   The successful completion of a sendto() does not indicate that
                the data was successfully delivered.
                
*/
int
base::sendto(const addr& to, const char* buf, int len, base::msgflag flags)
throw()
{
        int r = ::sendto(fd, buf, len, flags, to.data(), to.size());
        
        error = r==socket_error ? __GET_LAST_ERROR : 0;
        
        return r;
}


/**
        \param level    The level at which the option is defined. Must be
                        sol_socket or ipproto_tcp
        \param name     Socket option to be set
        \param value    The new value to set
        
        \return true if no error occurred.
        
        \sa setsockopt(), ioctl()
*/
bool
base::setsockopt(base::optlevel level, base::optname name, int value)
throw()
{
        int r = ::setsockopt(fd, level, name, reinterpret_cast<const char*>(&value), sizeof value);
        
        error = r? __GET_LAST_ERROR : 0;
        
        return !error;
}


/**
        \param how      A flag that describes what type of operation will no
                        longer be allowed.

        \return true if no error occurred.
        
        \note   This does not close the socket, and resources attached to
                the descriptor will not be freed until close() is invoked.
        \note   Never reuse a socket after it has been shut down (don't call
                connect() or accept() on it).

        \sa close()
*/
bool
base::shutdown(base::shuthow how)
throw()
{
        int r = ::shutdown(fd, how);
        
        error = !r ? __GET_LAST_ERROR : 0;
        
        return !error;
}


/**
        Allocates a socket descriptor of the specified address family, data
        type and protocol, as well as related resources. If a protocol is not
        specified (i.e. pf_unspec), the default for the specified connection
        mode is used.

        Only a single protocol exists to support a particular socket type using
        a given address format. However, the address family may be given as
        pf_unspec (unspecified), in which case the protocol parameter must be
        specified. The protocol value to use is particular to the
        "communication domain" in which communication is to take place.

        \param fmt      The address format specification. Use pf_inet if you
                        want the ARPA Internet address format
        \param tp       Type specification for the new socket. Common values are
                        sock_stream and sock_dgram.
        \param proto    A particular protocol to be used with the socket, or
                        proto_ip by default

        \note   sock_stream provides sequenced, reliable, two-way,
                connection-based byte streams with an out-of-band data
                transmission mechanism. Uses TCP for the Internet address family.
                They are full-duplex byte streams. A stream socket must be in a
                connected state before any data may be sent or received on it.
                A connection to another socket is created with a connect() call.
                Once connected, data may be transferred using send() and recv()
                calls. When a session has been completed, a closesocket() must
                be performed. Out-of-band data may also be transmitted as
                described in send() and received as described in recv().
                The communications protocols used to implement a sock_stram
                ensure that data is not lost or duplicated. If data for which
                the peer protocol has buffer space cannot be successfully
                transmitted within a reasonable length of time, the connection
                is considered broken and subsequent calls will fail with the
                error data member code set to etimedout.

        \note   sock_dgram supports datagrams, which are connectionless,
                unreliable buffers of a fixed (typically small) maximum length.
                Uses UDP for the Internet address family. sock_dgram sockets
                allow sending and receiving of datagrams to and from arbitrary
                peers using sendto() and recvfrom().  If such a socket is
                connect()ed to a specific peer, datagrams may be send to that
                peer send() and may be received from (only) this peer using recv().
*/
bool
base::create(base::format fmt, base::type tp, base::protocol proto)
throw()
{
        fd = ::socket(fmt, tp, proto);
        
        error = fd == invalid_socket ? __GET_LAST_ERROR : 0;
        
        return !error;
}


/**
        Convenience function that calls send() in a loop and
        stops only when either all data was sent or an error in the connection
        ocurred.
        
        \param buf     The buffer with data to send.
        \param len     The length of data in buf.
        \param flags   Flags to use in the send() call.
         
        \retval true   All data was sent.
        \retval false  The connection is closed or broken.
        
        \note   There's no way to know how much data was sent, and it's
                not useful anyways, as it only fails when the connection
                is lost. Non-blocking sockets will obviously block on this
                function.

        \sa send(), sendto()
*/
bool
base::sendall(const char* buf, int len, msgflag flags)
throw()
{
        int sent = 0;
        int i;
        while ((i = send(buf + sent, len - sent, flags)) != 0) {
                if (i<0)
                        if (error == ewouldblock) {
                                #ifdef WIN32
                                Sleep(1);
                                #else
                                usleep(1000);
                                #endif
                                continue;
                        } else
                                return false;
                sent += i;
        }
        return sent == len;
}


/**
        Convenience function that calls recv() in a loop until all data
        specified was received, or an error ocurred.
        
        \param buf      The buffer to store the data
        \param len      The length of the buffer
        \param flags    The flags to use in the recv() call.
        
        \retval true    All the requested data was received
        \retval false   The connection was closed or broken.
        
        \note   There's no way to know how much data was received, and it's not
                useful anyways, as it only fails when the connection is lost.
                Non-blocking sockets will obviously block on this function.
                
        \sa recv(), recvfrom()
*/
bool
base::recvall(char * buf, int len, msgflag flags)
throw()
{
        int rec = 0;
        int i;
        while ((i = recv(buf + rec, len - rec, flags)) != 0) {
                if (i<0)
                        if (error == ewouldblock) {
                                #ifdef WIN32
                                Sleep(1);
                                #else
                                usleep(1000);
                                #endif
                                continue;
                        } else
                                return false;
                rec += i;
        }
        return rec == len;
}


/**
        Convenience function that sets the blocking mode of the socket.
        
        \param bl       false for enabling non-blocking mode

        \return         true if no error ocurred.
*/
bool
base::block(bool bl)
throw()
{
        unsigned long val = bl;
        return ioctl(fionbio, val);
}


/**
        Convenience function that retrives the so_reuseaddr option through
        getsockopt().
        
        \return The value of the so_reuseaddr option; if an error ocurred
                it returns false. <b>The only way to detect errors is checking
                the error data member</b>.
*/
bool
base::reuseaddr() const
throw()
{
        bool val;
        if (getsockopt(sol_socket, so_reuseaddr, val))
                return val;
        else
                return false;
}


/**
        Convenience function that sets the so_reuseaddr option through
        setsockopt().
        
        \param reuse    Wheter the address should be reused or not.
        
        \return true if no error ocurred.
*/
bool
base::reuseaddr(bool reuse)
throw()
{
        return setsockopt(sol_socket, so_reuseaddr, reuse);
}


/**
        \return The size of the internal send() buffer.
*/
int
base::sendbuf() const
throw()
{
        int res = 0;
        getsockopt(sol_socket, so_sndbuf, res);
        return res;
}


/**
        \param size     The desired new size of the buffer.
        
        \note   Avoid changing the buffer size after the socket is connected, or
                else some information may be lost.
*/
void
base::sendbuf(int size)
throw()
{
        setsockopt(sol_socket, so_sndbuf, size);
}


/**
        \return The size of the internal recv() buffer.
*/
int
base::recvbuf() const
throw()
{
        int res = 0;
        getsockopt(sol_socket, so_rcvbuf, res);
        return res;
}


/**
        \param size     The desired new size of the buffer.

        \note   Avoid changing the buffer size after the socket is connected, or
                else some information may be lost.
*/
void
base::recvbuf(int size)
throw()
{
        setsockopt(sol_socket, so_rcvbuf, size);
}

}
